package rest

import (
	"bufio"
	"fmt"
	"monoblog/common"
	"monoblog/domain/dto"
	"monoblog/domain/service"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

func AccessLog(ctx *gin.Context) {
	var query dto.AccessLogQuery

	if err := ctx.ShouldBind(&query); err != nil {
		common.Fail(ctx, "Query数据绑定失败", err)
		return
	}

	page, err := service.GetAccessLogService().FindPage(ctx, query)
	if err != nil {
		common.Fail(ctx, "查询访问日志列表失败", err)
		return
	}
	common.Success(ctx, page)
}

// StreamConsole 通过SSE实时推送app.log日志内容
func StreamConsole(ctx *gin.Context) {
	// 1. 首先设置SSE响应头（必须在任何输出之前设置）
	ctx.Header("Content-Type", "text/event-stream; charset=utf-8")
	ctx.Header("Cache-Control", "no-cache")
	ctx.Header("Connection", "keep-alive")
	ctx.Header("Retry", "3000") // 客户端断开后3秒重连
	// 获取日志文件路径
	logPath := common.GetLogFilePath()
	if logPath == "" {
		// 以SSE格式返回错误
		ctx.Writer.WriteString("data: [error]获取日志文件路径失败\n\n")
		ctx.Writer.Flush()
		return
	}
	// 检查文件是否存在
	if _, err := os.Stat(logPath); os.IsNotExist(err) {
		ctx.Writer.WriteString(fmt.Sprintf("data: [error]日志文件不存在: %s\n\n", logPath))
		ctx.Writer.Flush()
		return
	}
	// 打开日志文件
	file, err := os.Open(logPath)
	if err != nil {
		ctx.Writer.WriteString(fmt.Sprintf("data: [error]打开日志文件失败: %v\n\n", err))
		ctx.Writer.Flush()
		return
	}
	defer file.Close()

	// 设置scanner缓冲区大小以处理大日志行
	const maxCapacity = 512 * 1024 // 512KB
	scannerBuf := make([]byte, maxCapacity)

	// 获取文件信息并设置初始位置到文件末尾
	fileInfo, err := file.Stat()
	var lastSize int64 = 0
	if err != nil {
		ctx.Writer.WriteString(fmt.Sprintf("data: [console]获取文件信息失败: %v\n\n", err))
		ctx.Writer.Flush()
	} else {
		lastSize = fileInfo.Size()
		// 从文件末尾开始读取，只显示新的日志内容
		if _, err := file.Seek(lastSize, 0); err != nil {
			ctx.Writer.WriteString(fmt.Sprintf("data: [console]定位文件指针失败: %v\n\n", err))
			ctx.Writer.Flush()
		}
	}
	// 创建连接关闭监听
	closeChan := ctx.Request.Context().Done()

	// 发送初始连接成功消息
	ctx.Writer.WriteString(fmt.Sprintf("data: [console]开始接收日志流 - %s\n\n", time.Now().Format("2006-01-02 15:04:05")))
	ctx.Writer.Flush()

	// 循环推送新增日志
	for {
		select {
		case <-closeChan:
			// 客户端断开连接，退出循环
			return
		default:
			// 获取最新的文件信息
			currentInfo, err := file.Stat()
			if err != nil {
				// 文件可能被删除或重命名，尝试重新打开
				ctx.Writer.WriteString("data: [console]文件状态检查失败，尝试重新连接...\n\n")
				ctx.Writer.Flush()

				// 关闭旧连接并尝试重新打开
				file.Close()

				file, err = os.Open(logPath)
				if err != nil {
					ctx.Writer.WriteString(fmt.Sprintf("data: [console]重新打开文件失败: %v\n\n", err))
					ctx.Writer.Flush()

					return //直接退出，避免无限重试
				} else {
					// 重新打开成功，现在调用readAndSendLogs函数读取内容
					// 读取新内容，输出日志文件的文本内容
					readAndSendLogs(ctx, file, scannerBuf)
					// 更新文件信息
					currentInfo, _ = file.Stat()
					if currentInfo != nil {
						lastSize = currentInfo.Size()
					}
				}
			} else if currentInfo.Size() < lastSize {
				// 文件被截断，重新读取
				ctx.Writer.WriteString("data: [console]日志文件已轮换，开始显示新内容...\n\n")
				ctx.Writer.Flush()
				file.Seek(0, 0) // 重新定位到文件开头
				lastSize = 0    // 重置文件大小
				// 调用函数读取新内容
				readAndSendLogs(ctx, file, scannerBuf)
			} else if currentInfo.Size() > lastSize {
				// 文件有新内容，重新定位并读取
				if _, err := file.Seek(lastSize, 0); err == nil {
					// 调用函数读取新增内容
					readAndSendLogs(ctx, file, scannerBuf)
				}
			}
			// 更新文件大小
			if currentInfo != nil {
				lastSize = currentInfo.Size()
			}
			// 无新日志时，短暂休眠避免CPU占用过高
			time.Sleep(100 * time.Millisecond)
		}
	}
}

// 移除 ANSI 终端颜色控制码的正则表达式
var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`)

// // StripAnsiCodes - 过滤 ANSI 控制码
//
//	func StripAnsiCodes(s string) string {
//		return ansiRegex.ReplaceAllString(s, "")
//	}
func readAndSendLogs(ctx *gin.Context, file *os.File, scannerBuf []byte) {
	scanner := bufio.NewScanner(file)
	scanner.Buffer(scannerBuf, cap(scannerBuf))

	// 读取并发送日志内容
	for scanner.Scan() {
		line := scanner.Text() //原始日志行
		// 后端去除ANSI颜色码（核心优化）
		pureLine := ansiRegex.ReplaceAllString(line, "")

		ctx.Writer.WriteString(fmt.Sprintf("data: [text]%s\n\n", pureLine))
		ctx.Writer.Flush()
	}

	if err := scanner.Err(); err != nil {
		ctx.Writer.WriteString(fmt.Sprintf("data: [console]读取日志失败: %v\n\n", err))
		ctx.Writer.Flush()
	}
}

// LogFileDownload 日志下载文件
func LogFileDownload(ctx *gin.Context) {
	// 获取日志文件路径
	logPath := common.GetLogFilePath()
	if logPath == "" {
		ctx.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "获取日志文件路径失败"})
		return
	}
	fileInfo, err := os.Stat(logPath)
	if err != nil {
		ctx.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "获取日志文件路径失败"})
		return
	}
	if fileInfo.IsDir() {
		ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "不支持下载目录"})
		return
	}

	logName := filepath.Base(logPath)

	ext := filepath.Ext(logName)
	name := strings.TrimSuffix(logName, ext)

	var renameLog = fmt.Sprintf("%s-%s%s", name, time.Now().Format("0102150405"), ext)

	// 设置响应头
	ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", renameLog))
	ctx.Header("Content-Type", "application/octet-stream;charset=utf-8")
	ctx.Header("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))

	// 发送文件内容
	http.ServeFile(ctx.Writer, ctx.Request, logPath)

}

func ManualPushBlog(ctx *gin.Context) {
	idsStr := ctx.PostForm("ids")
	if idsStr == "" {
		ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "ids不能为空"})
		return
	}
	blogIds := strings.Split(idsStr, ",")

	// 调用服务层方法
	successIds, err := service.GetArticleService().PushDataToSearch(ctx, blogIds)
	if err != nil {
		ctx.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// 成功推送的博客ID list
	common.Success(ctx, successIds)
}
